package viz;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import util.io.FileUtil;

public class Visualizer {
	public static void main(String[] args){
		//		for (String line: FileUtil.getTextFromFile("index.html").split("\n")){
		//			System.out.println("<a href='"+line+"'>"+line+"</a>");
		//		}
		//		System.out.println(convertToTree("(S1 (S (NP (NP (DT The) (NN discrepancy)) (PP (IN in) (NP (NP (DT the) (NN degree)) (PP (IN of) (NP (NP (NN inhibition)) (PP (IN by) (NP (NP (NN membrane) (NNS extracts)) (CC and) (NP (JJ NK) (NNS cells)))) (PP (IN with) (NP (JJ mutant) (NNP TNFRI)))))))) (VP (VBZ suggests) (SBAR (IN that) (S (NP (JJ additional) (NNS pathways)) (VP (AUX are) (VP (VBN utilized) (PP (IN by) (NP (JJ NK) (NNS cells))) (S (VP (TO to) (VP (VBP activate) (NP (NNP EC)))))))))) (. .)))"));
		//				System.exit(-1);
		HashSet<String> visited = new HashSet<String>();
		String path = "data/BioNLP09/dev/";
		for (String file: new File(path).list()){
			if (file.contains(".")){
				String prefix = file.substring(0, file.indexOf("."));
				if (!visited.contains(prefix)){
					printHtml(path+prefix, "html/"+prefix+".html");
					visited.add(prefix);
				}
			}
		}
//		String prefix = "9149909";
//		printHtml(path+prefix, "html/"+prefix+".html");
	}

	/**
	 * given a1(Protein), a2(Event), txt files
	 * print out html file which visualize the annotation 
	 * @param filePrefix : including the path
	 */
	public static void printHtml(String filePrefix){
		printHtml(filePrefix, filePrefix+".html");
	}

	/**
	 * 
	 * @param filePrefix
	 * @param output : file including path
	 */
	public static void printHtml(String filePrefix, String output){
		String text = FileUtil.getTextFromFile(filePrefix+ ".txt");
		String[] proteins = FileUtil.getTextFromFile(filePrefix + ".a1").split("\n");
		String[] events = FileUtil.getTextFromFile(filePrefix+".a2").split("\n");
		ArrayList<Insertion> list = new ArrayList<Insertion>();
		for (String protein:proteins){
			String[] items = protein.split("\t");
			Insertion ins = new Insertion();
			ins.tag = "<sup>"+items[0]+"</span></sup>";
			if (items.length < 2){
				//				System.err.println(protein);
				continue;
			}
			String[] pos = items[1].split(" ");
			ins.begin = Integer.parseInt(pos[1]);
			ins.end = Integer.parseInt(pos[2]);
			ins.text = "<span id='"+items[0]+"'>";//items[2];
			//			if (!ins.check(text))
			//				System.err.println(protein +" extracts wrong!");
			//			else
			list.add(ins);
		}
		ArrayList<String> eventAnnotations = new ArrayList<String>();
		// key = mention id, value = cluster of mentions
		Hashtable<String, HashSet<String>> coref = new Hashtable<String, HashSet<String>>();
		// event  id strings
		Hashtable<String, String> id2str = new Hashtable<String, String>();
		for (String event : events){
			if (event.startsWith("T")){
				String[] items = event.split("\t");
				Insertion ins = new Insertion();
				ins.tag = "<sup>"+items[0]+"</sup></span>";
				String[] pos = items[1].split(" ");
				ins.begin = Integer.parseInt(pos[1]);
				ins.end = Integer.parseInt(pos[2]);
				ins.text = "<span id='"+items[0]+"'>";//items[2];
				//				if (!ins.check(text))
				//					System.err.println(event +" extracts wrong!");
				//				else
				list.add(ins);
				id2str.put(items[0], items[2]);
			}
			else{
				// events or speculations or equivalence
				StringBuilder sb = new StringBuilder();
				String[] items = event.split("\\s+");
				if (items[0].startsWith("M")){
					sb.append(items[0]+"            "+items[1]+"          ");
					sb.append("<a href='' onClick='return changecolor(\""+items[2]+"\")'>"+items[2]+"</a>");
				}
				else if (items[0].startsWith("*")){
					if (items.length == 4){
						sb.append(items[0]+"    "+items[1] + "         " +"<a href='' onClick='return changecolor(\""+items[2]+"\")'>"+items[2]+"</a>"
								+ "            <a href='' onClick='return changecolor(\""+items[3]+"\")'>"+items[3]+"</a>");
						// building coref data
						if (!coref.containsKey(items[2])){
							coref.put(items[2], new HashSet<String>());
							coref.get(items[2]).add(items[2]);
						}
						if (!coref.containsKey(items[3])){
							coref.put(items[3], new HashSet<String>());
							coref.get(items[3]).add(items[3]);
						}
						coref.get(items[2]).addAll(coref.get(items[3]));
						for (String id:coref.get(items[3])){
							coref.put(id, coref.get(items[2]));
						}
					}else{
						sb.append("!!!"+event);
					}
				}
				else{
					sb.append("<span id='"+items[0]+"'>"+items[0]+"</span>        ");
					if (items.length < 2){
						System.err.println(event);
						continue;
					}
					for (int  i = 1; i < items.length; i++){
						String entity = items[i].substring(items[i].indexOf(":")+1);
						sb.append("         "+items[i].replace(entity,"<a href='' onClick='return changecolor(\""+entity+"\")'>"+entity+"</a>"));
					}

					//					entity = items[2].substring(items[2].indexOf(":")+1);
					//					sb.append("         "+items[2].replace(entity,"<a href='' onClick='return changecolor(\""+entity+"\")'>"+entity+"</a>"));


				}
				eventAnnotations.add(sb.toString());
			}
		}

		// get events and their strings
		Hashtable<String, String> str2event = getEventStrings(events, id2str, coref);

		// swap the themes for binding type
		Pattern p = Pattern.compile("Theme(T\\d+)Theme2(T\\d+)");
		Hashtable<String, String> toAdd = new Hashtable<String, String>();
		for (String key: str2event.keySet()){
			Matcher m = p.matcher(key);
			if (m.find()){
				toAdd.put(key.replace(m.group(), "Theme"+m.group(2)+"Theme2"+m.group(1)), str2event.get(key));
			}
		}
		str2event.putAll(toAdd);

		// insert superscripts
		Collections.sort(list);
		StringBuilder sb = new StringBuilder(text);

		for (int i = list.size() -1 ; i >-1; i--){
			sb.insert(list.get(i).end, list.get(i).tag);
			sb.insert(list.get(i).begin, list.get(i).text);
		}
		sb.insert(0, "<html><head><script type='text/javascript' src='script.js'> </script></head><body><p>");
		sb.append("</p>");

		sb.append("<table border=1><tr><td>truth</td><td>prediction</td></tr><tr><td>");
		sb.append("<p>");
		for (String event: eventAnnotations){
			sb.append(event+"<br/> \n");
		}
		sb.append("</p>");
		sb.append("</td><td>");

		showPrediction(filePrefix, sb, str2event);

		sb.append("</td></tr></table>");

		showParse(filePrefix, sb);

		sb.append("</body></html>");
		FileUtil.writeTextToFile(sb.toString(), output);
	}

	private static Hashtable<String, String> getEventStrings(String[] events, Hashtable<String, String> id2str,
			Hashtable<String, HashSet<String>> coref) {
		System.out.println("coref "+coref);
		Hashtable<String, String> str2event = new Hashtable<String, String>();
		// avoid the case where two events share the same string, because of that I omit the site and atLoc, etc
		// for example, 
		// E1      Positive_regulation:T9 Theme:T5  Site:T10
		// E2      Positive_regulation:T9 Theme:T5  Site:T11
		Hashtable<String, String> sameStringIds = new Hashtable<String, String>();

		// get the gold strings in 2 passes
		String[] lines = events;
		for (int i = 0; i < lines.length ; i++){
			if (lines[i].startsWith("E")){
				String[] items = lines[i].split("\t|:| ");
				String event = items[0];
				ArrayList<StringBuilder> strList = new ArrayList<StringBuilder>();
				strList.add(new StringBuilder());
				for (int j =1; j < items.length; j++){
					if (id2str.containsKey(items[j])){
						for (int k = 0; k < strList.size(); k++){
							StringBuilder str = strList.get(k);
							str.append( id2str.get(items[j]));
						}
					}
					else if (items[j].matches("E\\d+")){
						for (int k = 0; k < strList.size(); k++){
							StringBuilder str = strList.get(k);
							str.append( "<"+items[j]+">");
						}
					}
					else if (items[j].equals("Site") || items[j].equals("CSite")||items[j].equals("AtLoc")|| items[j].equals("ToLoc")){
						//						items[0]+=items[j]+items[j+1];
						j++;
					}
					else{
						if (coref.containsKey(items[j])){

							System.out.println(items[j] + " found by coref");
							HashSet<StringBuilder> tmp = new HashSet<StringBuilder>();
							//							for (int k = 0; k < strList.size(); k++){
							//								StringBuilder str = strList.get(k);
							//								tmp.add(str);
							//								str.append(items[j]);
							//							}
							for (String otherMention: coref.get(items[j])){
								//								if (otherMention.equals(items[j]))
								//									continue;
								for (StringBuilder str: strList)
									tmp.add(new StringBuilder(str.toString() +otherMention));
							}
							strList.clear();
							strList.addAll(tmp);
						}
						else{
							for (int k = 0; k < strList.size(); k++){
								StringBuilder str = strList.get(k);
								str.append(items[j]);
							}
						}
					}
				}
				for (StringBuilder eventStr: strList){
					if (str2event.containsKey(eventStr.toString())){
						//						String id1 = 	str2event.get(eventStr.toString());
						//						sameStringIds.put(id1, items[0]);
						//						sameStringIds.put(items[0], id1);
						str2event.put(eventStr.toString(), str2event.get(eventStr.toString())+","+items[0]);
					}
					else{
						str2event.put(eventStr.toString(), items[0]);
					}

				}
			}
		}
		System.err.println("before replacing..."+ str2event);
		boolean flag = true;
		int c = 0;
		while (flag){
			flag = false;
			Hashtable<String, String> toAdd = new Hashtable<String, String>(), toRemove = new Hashtable<String, String>();
			for (Entry<String, String> entry: str2event.entrySet()){
				String str = entry.getKey();
				if (str.contains("<")){
					flag = true;
					String id = str.substring(str.indexOf("<")+1,str.indexOf(">"));
					boolean found = false;
					for (Entry<String, String> entry2: str2event.entrySet()){
						String[] ids = entry2.getValue().split(",");
						for (String eid : ids){
							if (eid.equals(id)){
								//							entry.setValue(str.replace("<"+id+">", str2event.get(id)));
								toAdd.put(str.replace("<"+id+">",entry2.getKey()), entry.getValue());
								toRemove.put(str, id);
								found = true;
							}
						}
					}
					if (!found){
						// if not found, check if any other event share the same string
						//						if (sameStringIds.containsKey(id)){
						//							found = false;
						//							id = sameStringIds.get(id);
						//							for (Entry<String, String> entry2: str2event.entrySet()){
						//								if (entry2.getValue().equals(id)){
						//									//							entry.setValue(str.replace("<"+id+">", str2event.get(id)));
						//									toAdd.put(str.replace("<"+id+">",entry2.getKey()), entry.getValue());
						//									toRemove.put(str, id);
						//									found = true;
						//								}
						//							}
						//						}
						if (!found)
							System.err.println(id + "not found!!");

					}
				}
			}
			str2event.putAll(toAdd);
			for (String key: toRemove.keySet()){
				str2event.remove(key);
			}

			c++;
			if (c > 50){
				System.err.println("dead loop");
				break;
			}
		}

		System.err.println("after replacing..."+ str2event);
		return str2event;

	}

	public static class Insertion implements Comparable{
		public int begin = -1, end = -1;
		public String text = null;
		public String tag = null;

		public boolean check(String fulltext){
			if (fulltext.substring(begin, end).equals(text)){
				return true;
			}
			else
				return false;
		}

		@Override
		public int compareTo(Object arg0) {
			// TODO Auto-generated method stub
			Insertion ins = (Insertion) arg0;
			if (begin < ins.begin)
				return -1;
			else if (begin == ins.begin){
				if (end < ins.end)
					return -1;
				else if (end == ins.end)
					return 0;
				else
					return 1;
			}
			else 
				return 1;
		}

	}

	public static final String devParsePath = "bionlp09_shared_task_devel_analyses_rev2/McClosky-Charniak";
	/**
	 * TODO
	 * Devel data!
	 * choose format in McClosky, or Stanford...
	 * 
	 */
	public static void showParse(String filePrefix, StringBuilder sb){
		String path = "data/BioNLP09/"+devParsePath + "/";
		String[] parses = FileUtil.getTextFromFile(path + "pstree/"+filePrefix.substring(filePrefix.lastIndexOf("/")+1)+".pstree").split("\n");
		String[] deps = FileUtil.getTextFromFile(path + "dep/" + filePrefix.substring(filePrefix.lastIndexOf("/")+1) +".dep").split("\n");
		int j = 0;
		for (int i = 0; i < parses.length; i++){
			sb.append("<p>");
			sb.append(convertToTree(parses[i]));
			sb.append("</p>");
			sb.append("<p>");
			while (j < deps.length && !deps[j].equals("")){
				sb.append(deps[j++]+"<br />");
			}
			j++;
			sb.append("</p>");
		}
	}

	/**
	 * break the parse tree into lines with indentations
	 * @param text
	 * @return
	 */
	private static String convertToTree(String text){
		String lineBreak = "<br />\n";
		String indent = "&nbsp";
		StringBuilder sb = new StringBuilder();
		int start = 0, prev = 0;
		int level = 0;

		while ( (start = text.indexOf("(", start+1))!= -1){

			String tag = text.substring(prev+1, text.indexOf(" ", prev+1));
			if (tag.endsWith("P")|| tag.startsWith("S")|| tag.equals("PRN"))
				sb.append( lineBreak);
			for (int k  = 0; k <level; k++)
				sb.append(indent);
			sb.append(text.substring(prev, start));
			String tmp = text.substring(prev,start).trim();
			int p = tmp.length() - 1;
			while (tmp.charAt(p) == ')'){
				level--;
				p --;
			}
			level++;
			prev = start;
		}
		return sb.toString();
	}

	public static final String predPath = "uturku/TurkuEventExtractionSystem-1.0/output2/geniaformat/";
	/**
	 * @param filePrefix
	 * @param sb
	 * @param str2event: key = event string, value = event id in gold; because the same event might have multiple true string due to coref protein names
	 * show UTurku's results
	 */
	public static void showPrediction(String filePrefix, StringBuilder sb, Hashtable<String, String> str2event){
		String[] lines = FileUtil.getTextFromFile(predPath + filePrefix.substring(filePrefix.lastIndexOf("/")+1)  + ".a2.t1").split("\n");
		Hashtable<String, String> id2str = new Hashtable<String, String>();
		for (String line: lines){
			if (line.startsWith("T")){
				String[] items = line.split("\\s+");
				id2str.put(items[0], items[items.length-1]);
			}
		}

		// get the prediction strings in 2 passes
		Hashtable<String, String> event2str = new Hashtable<String, String>();
		for (int i = 0; i < lines.length ; i++){
			if (lines[i].startsWith("E")){
				String[] items = lines[i].split("\t|:| ");
				String event = items[0];
				String str = "";
				for (int j =1; j < items.length; j++){
					if (id2str.containsKey(items[j])){
						str += id2str.get(items[j]);
					}
					else if (items[j].matches("E\\d+")){
						str += "<"+items[j]+">";
					}
					else{
						if (items[j].equals("Theme1"))
							str += "Theme";
						else
							str += items[j];
					}
				}
				event2str.put(event, str);
			}
		}
		boolean flag = true;
		int c = 0;
		while (flag){
			flag = false;
			for (Entry<String, String> entry: event2str.entrySet()){
				String str = entry.getValue();
				if (str.contains("<")){
					flag = true;
					String id = str.substring(str.indexOf("<")+1,str.indexOf(">"));
					if (event2str.containsKey(id)){
						entry.setValue(str.replace("<"+id+">", event2str.get(id)));
					}
					else{
						System.err.println(id + "not found!!");
					}
				}
			}
			c++;
			if (c > 50){
				System.err.println("dead loop");
				break;
			}
		}
		System.err.println("filePrefix = "+filePrefix);
		System.err.println(event2str);
		System.err.println("=========");
		System.err.println(str2event);
		System.err.println("====END===");

		// print
		for (int i = 0; i < lines.length ; i++){
			if (lines[i].startsWith("E")){
				String[] items = lines[i].split("\t|:| ");
				for (int j  = 0; j < items.length; j++){
					sb.append(items[j]);
					if (items[j].startsWith("T")&& id2str.containsKey(items[j])){
						sb.append("("+id2str.get(items[j])+")");
					}
					sb.append("&nbsp&nbsp");
				}
				if (str2event.containsKey(event2str.get(items[0]))){
					sb.append("<b>Correct "+str2event.get(event2str.get(items[0]))+" in GOLD </b>");
				}
				else{
					sb.append("<b><font color=red>Wrong!</font></b>");
				}

				sb.append("<br />\n");
			}
			else{
				sb.append(lines[i]+"<br />\n");
			}
		}

	}
}
